#include <iostream>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <strings.h>
#include <unistd.h>

#include "Comm.hpp"
#include "log.hpp"

#define Retry_Count 5

void Usage(std::string proc)
{
    std::cout << "Usage: " << proc << " local_ip  local_port"
              << std::endl;
}

// 访问服务器
bool visitServer(std::string server_ip, uint16_t server_port, int *cnt)
{
    bool running_result = true; // 记录运行中是否出错

    // 2. 创建 socked
    // Tcp 协议 , 面向字节流
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd < 0)
    {
        lg.LogMessage(Fatal, "Create socket Error!");
        return 0;
    }

    // 填写 Addr 结构体
    struct sockaddr_in server;
    bzero(&server, sizeof server); // 将结构体清零

    server.sin_family = AF_INET;
    server.sin_port = htons(server_port);

    // 设置 IP 地址
    inet_pton(AF_INET, server_ip.c_str(), &server.sin_addr);

    // 需要 bind ,但是不需要手动 bind, 主机会自动选择合适的端口进行bind

    // 进行 connect 链接
    int n = connect(sockfd, (const struct sockaddr *)&server, (socklen_t)sizeof server);
    if (n < 0)
    {
        lg.LogMessage(Fatal, "connet error!");

        // 关闭文件描述符
        close(sockfd);
        return 0;
    }

    *cnt = 0; // 到这里为连接成功，重连次数置为 0

    // 选择服务
    {
        
        std::string buffer;
    
        flag:
        std::cout << "Please Select Routine: " << std::endl;
        do
        {
            getline(std::cin, buffer); // 防止回车问题导致服务端认为客户端断线
        } while (buffer.size() == 0);

        if (buffer == "/q!")
        {
            close(sockfd);
            return running_result;
        }

       
        ssize_t n = write(sockfd, buffer.c_str(), buffer.size());
        if (n > 0)
        {
            char rev_buf[16];
            size_t m = read(sockfd, rev_buf, sizeof rev_buf - 1);
            if (m > 0)
            {
                rev_buf[m]=0;
                if(std::string(rev_buf)=="no serve !")
                {
                    std::cout<<"no serve !"<<std::endl;
                    goto flag;
                }
            }
            else
            {
                close(sockfd);
                return 0;
            }
        }
        else
        {
            close(sockfd);
            return 0;
        }
    }

    while (true)
    {

        std::string buffer;
        std::cout << "Please Input: " << std::endl;
        do
        {
            getline(std::cin, buffer); // 防止回车问题导致服务端认为客户端断线
        } while (buffer.size() == 0);

        // std::cin>>buffer;
        // std::cin.get();

        if (buffer == "/q!")
            break;

        ssize_t n = write(sockfd, buffer.c_str(), buffer.size());
        if (n > 0)
        {

            std::string message;
            // message += "server say# ";

            char buffer[1024];
            size_t m = read(sockfd, buffer, sizeof buffer - 1);

            if (m > 0)
            {
                buffer[m] = 0;
                message += buffer;
                std::cout << message << std::endl;
            }
            else if (m == 0) // == 0 说明读写时对端把连接关闭了
            {
                running_result = 0;
                break;
            }
            else
            {
                running_result = 0;
                break; // 读取错误
            }
        }
        else
        {
            // 写入失败，返回 false
            running_result = 0;
            break;
        }
    }

    close(sockfd);
    return running_result;
}

int main(int argv, char *argc[])
{
    if (argv != 3)
    {
        Usage(argc[0]);
        return Usage_Err;
    }

    // 1. 读取 ip 和 端口信息
    std::string server_ip = argc[1];
    uint16_t server_port = std::stoi(argc[2]);

    int cnt = 1;

    while (cnt <= Retry_Count) // 尝试默认次数的重连
    {
        // 访问服务器
        bool result = visitServer(server_ip, server_port, &cnt);

        if (result)
        {
            // 正常退出
            break; // 退出程序
        }
        else
        {
            // 非正常退出
            sleep(1);
            std::cout << "server offline, retrying ... , count : " << cnt++ << std::endl;
        }
    }

    return 0;
}